package by.bsu.wp.ntfsp.builder;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import org.jgrapht.DirectedGraph;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;

import by.bsu.wp.ntfsp.exception.NFRSProblemInconsistantException;
import by.bsu.wp.ntfsp.model.NotFullRankSystemProblem;
import by.bsu.wp.ntfsp.model.ProductFlowEdge;

public class NFRSProblemBuilder {
	
	private NotFullRankSystemProblem problem;

	public NFRSProblemBuilder() {
		this.problem = new NotFullRankSystemProblem();
		this.problem.setSourceNet(new DefaultDirectedGraph<Integer, DefaultEdge>(DefaultEdge.class));
		this.problem.setProducts(new HashSet<Integer>());
		this.problem.setProductFlowNets(new HashMap<Integer, DirectedGraph<Integer, ProductFlowEdge>>());
		this.problem.setBalances(new HashMap<Integer, Map<Integer, Integer>>());
		this.problem.setZeroEdges(new HashMap<DefaultEdge, Integer>());
		this.problem.setBaseEquationTotals(new HashMap<Integer, Integer>());
	}

	public void addEdge(Integer sourceVertex, Integer targetVertex) {
		this.problem.getSourceNet().addVertex(sourceVertex);
		this.problem.getSourceNet().addVertex(targetVertex);
		this.problem.getSourceNet().addEdge(sourceVertex, targetVertex);
	}

	public void createProductFlowNets(Integer numberOfProducts) {
		for (int product = 1; product <= numberOfProducts; product++) {
			this.problem.getProductFlowNets()
					.put(product, new DefaultDirectedGraph<Integer, ProductFlowEdge>(ProductFlowEdge.class));
			this.problem.getBalances().put(product, new HashMap<Integer, Integer>());
			this.problem.getProducts().add(product);
		}
	}

	public void addEdgeForProduct(Integer product, Integer sourceVertex, Integer targetVertex)
			throws NFRSProblemInconsistantException {
		if (!this.problem.getSourceNet().containsEdge(sourceVertex, targetVertex)) {
			throw new NFRSProblemInconsistantException("Edge (" + sourceVertex + ", " + targetVertex
					+ ") is absent in the source net.");
		}

		if (!this.problem.getProducts().contains(product)) {
			throw new NFRSProblemInconsistantException("Flow net for product " + product + " does not exist.");
		}

		ProductFlowEdge edge = new ProductFlowEdge();
		this.problem.getProductFlowNets().get(product).addVertex(sourceVertex);
		this.problem.getProductFlowNets().get(product).addVertex(targetVertex);
		this.problem.getProductFlowNets().get(product).addEdge(sourceVertex, targetVertex, edge);
	}
	
	public void markZeroEdge(Integer sourceVertex, Integer targetVertex) throws NFRSProblemInconsistantException {
		if (!this.problem.getSourceNet().containsEdge(sourceVertex, targetVertex)) {
			throw new NFRSProblemInconsistantException("Edge (" + sourceVertex + ", " + targetVertex
					+ ") is absent in the source net.");
		}

		this.problem.getZeroEdges().put(this.problem.getSourceNet().getEdge(sourceVertex, targetVertex), null);
	}

	public void setNumberOfBaseEquations(int numberOfBaseEquations) {
		this.problem.setNumberOfBaseEquations(numberOfBaseEquations);
	}

	public void setLambdaForProductEdge(Integer product, Integer sourceVertex, Integer targetVertex, int p, int lambda)
			throws NFRSProblemInconsistantException {

		if (!this.problem.getProducts().contains(product)) {
			throw new NFRSProblemInconsistantException("Flow net for product " + product + " does not exist.");
		}

		if (!this.problem.getProductFlowNets().get(product).containsEdge(sourceVertex, targetVertex)) {
			throw new NFRSProblemInconsistantException("Edge (" + sourceVertex + ", " + targetVertex
					+ ") is absent in the flow net for product " + product);
		}

		this.problem.getProductFlowNets().get(product).getEdge(sourceVertex, targetVertex).setLambdaCoefficient(p, lambda);
	}

	public void setVertexBalanceForProduct(Integer product, Integer vertex, Integer balance)
			throws NFRSProblemInconsistantException {
		if (!this.problem.getProducts().contains(product) || !this.problem.getProductFlowNets().keySet().contains(product)) {
			throw new NFRSProblemInconsistantException("Flow net for product " + product + " does not exist.");
		}

		if (!this.problem.getProductFlowNets().get(product).containsVertex(vertex)) {
			throw new NFRSProblemInconsistantException("Flow net for product " + product + " does not contain vertex "
					+ vertex);
		}

		this.problem.getBalances().get(product).put(vertex, balance);
	}

	public void setTotalForBaseEquation(int p, int alpha) throws NFRSProblemInconsistantException {
		this.problem.getBaseEquationTotals().put(p, alpha);
	}

	public void setTotalForZeroEdge(Integer sourceVertex, Integer targetVertex, Integer z)
			throws NFRSProblemInconsistantException {
		if (!this.problem.getSourceNet().containsEdge(sourceVertex, targetVertex)) {
			throw new NFRSProblemInconsistantException("Edge (" + sourceVertex + ", " + targetVertex
					+ ") is absent in the source net.");
		}
		
		if (!this.problem.getZeroEdges().keySet().contains(this.problem.getSourceNet().getEdge(sourceVertex, targetVertex))) {
			throw new NFRSProblemInconsistantException("Edge (" + sourceVertex + ", " + targetVertex
					+ ") is absent in U_0 set.");
		}
		
		this.problem.getZeroEdges().put(this.problem.getSourceNet().getEdge(sourceVertex, targetVertex), z);
	}
	
	public NotFullRankSystemProblem getProblem() {
		return problem;
	}
	
}
